package wiki.old.cs236703.Wiki.Implementation;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import wiki.old.cs236703.Wiki.AbstractWikiContentManager;
import wiki.old.cs236703.Wiki.ContentRanker;
import wiki.old.cs236703.Wiki.ContentType;
import wiki.old.cs236703.Wiki.ContentRanker.QueryException;


public class SimpleWikiContentManager extends AbstractWikiContentManager{
	Map<Long, Stack<SimpleWikiContent> > wikiContentMap = new HashMap<Long, Stack<SimpleWikiContent> >();
	Map<String, Long> nameToIdMap = new HashMap<String, Long>();
	
	long id = 0;
	
	private void generateNewID(){
		while(wikiContentMap.containsKey(id)){
			++id;
		}
	}	
	
	final private class SimpleWikiContent implements WikiContent, Cloneable{	
		String name;
		ContentType contentType;
		byte[] content;
		int revision;
		long id;		
		Map<ContentAttributes, String> attributes = 
			new EnumMap<ContentAttributes, String>(ContentAttributes.class);
		
		public SimpleWikiContent(long id, String name) {
			this(id, name, 0);
		}
		
		public SimpleWikiContent(long id, String name, int revision) {
			this.id = id;
			this.name = name;
			this.revision = revision;
		}
//		@Override
//		public ContentType contentType() {
//			return contentType;
//		}		
		@Override
		public String name() {
			return name;
		}
		@Override
		public byte[] content() {
			return content;
		}
		@Override
		public int revision() {
			return revision;
		}
		@Override
		public Map<ContentAttributes, String> getAttributes() {
			return attributes;
		}
//		@Override
//		public String getAttributes(ContentAttributes key) {
//			return attributes.get(key);
//		}
//		@Override
//		public void setContent(byte[] content, ContentType contentType) {
//			if (SimpleWikiContentManager.this.contentProcessorMap.containsKey(contentType))
//				this.content = 
//					SimpleWikiContentManager.this.contentProcessorMap.get(contentType).process(content);
//			else
//				this.content = content;
//			this.contentType = contentType;			
//		}
		@Override
		public void setAttributes(Map<ContentAttributes, String> attributes) {
			this.attributes.putAll(attributes);
		}	
		@Override
		public void setAttribute(ContentAttributes key, String value) {
			this.attributes.put(key, value);
		}
		@Override
		public void setName(String name) {
			this.name = name;			
		}

		@Override
		public void setContent(byte[] content, String contentType) {
			// TODO Auto-generated method stub
			
		}

		@Override
		public String getAttribute(ContentAttributes key) {
			// TODO Auto-generated method stub
			return null;
		}	
	}	
	
	@Override
	public WikiContent getWikiContent(String name) {
		if (hasWikiContent(name))
			return wikiContentMap.get(nameToIdMap.get(name)).peek();
		Stack<SimpleWikiContent> contentStack = new Stack<SimpleWikiContent>();
		generateNewID();
		nameToIdMap.put(name, id);
		SimpleWikiContent newSimpleWikiContent = new SimpleWikiContent(id, name);
		contentStack.push(newSimpleWikiContent);
		wikiContentMap.put(id, contentStack);
		return newSimpleWikiContent;
	}

	@Override
	public boolean hasWikiContent(String name) {
		return wikiContentMap.containsKey(name);
	}

//	@Override
//	public ArrayList<SearchResult> search(String query) throws QueryException {
//		for (ContentRanker ranker: contentRankerMap.values()){
//			ranker.setQuery(query);
//		}		
//		ArrayList<SearchResult> results = new ArrayList<SearchResult>();
//		for(Stack<SimpleWikiContent> contentStack: wikiContentMap.values()){
//			WikiContent wikiContent = contentStack.peek();
//			int rank = 0;
//			if (contentRankerMap.containsKey(wikiContent.contentType()))
//				rank = contentRankerMap.get(wikiContent.contentType()).rank(wikiContent.content());
//			if (rank > 0)
//				results.add(new SearchResult(rank, wikiContent));
//		}	
//		return results;
//	}

	private SimpleWikiContent createRevision(long id, WikiContent c){		
		Stack<SimpleWikiContent> contentStack = wikiContentMap.get(id);
		SimpleWikiContent other = new SimpleWikiContent(id, c.name(), contentStack.size());
		other.attributes = new EnumMap<ContentAttributes, String>(ContentAttributes.class);
		other.attributes.putAll(c.getAttributes());
		other.content = c.content().clone();
		//other.contentType = c.contentType();
		nameToIdMap.remove(c.name());
		nameToIdMap.put(c.name(), id);
		return other;		
	}
	
	@Override 
	synchronized public WikiContent update(WikiContent c) {
		Stack<SimpleWikiContent> contentStack = null;
		if (!c.getClass().equals(SimpleWikiContent.class)){
			contentStack = new Stack<SimpleWikiContent>();
			generateNewID();
			wikiContentMap.put(id, contentStack);
			return createRevision(id, c);			
		}
		SimpleWikiContent c1 = (SimpleWikiContent)c;
		if (!wikiContentMap.containsKey(c1.id))
			contentStack = new Stack<SimpleWikiContent>();
			generateNewID();
			c1.id = id;			
			wikiContentMap.put(id, contentStack);
		return createRevision(id, c);	
	}

	@Override
	public boolean isWikiContentValid(WikiContent c) {
		return (wikiContentMap.containsKey(c.name()) && wikiContentMap.get(c.name()).size() == c.revision());
	}

	@Override
	public WikiContent rollBackToRevision(WikiContent c, int targetRevision) throws RollBackException {
		if (targetRevision < 0 || !wikiContentMap.containsKey(c.name()))
			throw new RollBackException();
		Stack<SimpleWikiContent> contentStack = wikiContentMap.get(c.name());
		if (contentStack.size() < targetRevision)
			throw new RollBackException();
		nameToIdMap.remove(contentStack.peek().name());
		long id = contentStack.peek().id;
		while (contentStack.size() > targetRevision + 1){
			contentStack.pop();
		}
		nameToIdMap.put(contentStack.peek().name(), id);
		return contentStack.peek();
	}

	@Override
	public ArrayList<SearchResult> search(String query) throws QueryException {
		// TODO Auto-generated method stub
		return null;
	}

}
